package com.zc.component.network.http.server.vertx;

import com.zc.component.network.core.*;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import lombok.extern.slf4j.Slf4j;
import com.zc.web.bean.FastBeanCopier;
import com.zc.core.metadata.ConfigMetadata;
import com.zc.core.metadata.DefaultConfigMetadata;
import com.zc.core.metadata.types.BooleanType;
import com.zc.core.metadata.types.IntType;
import com.zc.core.metadata.types.ObjectType;
import com.zc.core.metadata.types.StringType;
import com.zc.component.network.core.security.CertificateManager;
import com.zc.component.network.core.security.VertxKeyCertTrustOptions;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;

/**
 * 使用Vertx实现HTTP服务
 *
 */
@Component
@Slf4j
public class HttpServerProvider implements NetworkProvider<HttpServerConfig> {

    private final CertificateManager certificateManager;

    private final Vertx vertx;

    public HttpServerProvider(CertificateManager certificateManager, Vertx vertx) {
        this.certificateManager = certificateManager;
        this.vertx = vertx;
    }

    @Nonnull
    @Override
    public NetworkType getType() {
        return DefaultNetworkType.HTTP_SERVER;
    }

    /**
     * 使用配置创建一个网络组件
     *
     * @param config 配置信息
     * @return 网络组件
     */
    @Nonnull
    @Override
    public VertxHttpServer createNetwork(@Nonnull HttpServerConfig config) {
        VertxHttpServer server = new VertxHttpServer(config);
        initServer(server, config);
        return server;
    }

    /**
     * 重新加载网络组件
     *
     * @param network    网络组件
     * @param config 配置信息
     */
    @Override
    public void reload(@Nonnull Network network, @Nonnull HttpServerConfig config) {
        VertxHttpServer server = ((VertxHttpServer) network);
        initServer(server, config);
    }

    /**
     * 创建 Http 服务
     * @param config Http Server配置信息
     * @return Http 客户端
     */
    protected HttpServer createHttpServer(HttpServerConfig config) {
        return vertx.createHttpServer(config.getOptions());
    }

    @Nullable
    @Override
    public ConfigMetadata getConfigMetadata() {
        return new DefaultConfigMetadata()
            .add("id", "id", "", new StringType())
            .add("instance", "服务实例数量(线程数)", "", new IntType())
            .add("certId", "证书id", "", new StringType())
            .add("ssl", "是否开启ssl", "", new BooleanType())
            .add("port", "请求服务端口", "", new IntType())
            .add("httpHeaders", "请求头", "", new ObjectType());
    }

    @Nonnull
    @Override
    public Mono<HttpServerConfig> createConfig(@Nonnull NetworkProperties properties) {
        return Mono.defer(() -> {
            HttpServerConfig config = FastBeanCopier.copy(properties.getConfigurations(), new HttpServerConfig());
            config.setId(properties.getId());
            if (config.getOptions() == null) {
                config.setOptions(new HttpServerOptions());
            }
            config.getOptions().setHost(config.getHost());
            config.getOptions().setPort(config.getPort());

            if (config.isSsl() && StringUtils.hasText(config.getCertId())) {
                config.getOptions().setSsl(true);
                return certificateManager
                    .getCertificate(config.getCertId())
                    .map(VertxKeyCertTrustOptions::new)
                    .doOnNext(config.getOptions()::setKeyCertOptions)
                    .doOnNext(config.getOptions()::setTrustOptions)
                    .thenReturn(config);
            }
            return Mono.just(config);
        });
    }

    /**
     * 初始化并启动一个或多个Vert.x HTTP服务器实例
     * @param server Http 服务
     * @param config 服务配置
     */
    private void initServer(VertxHttpServer server, HttpServerConfig config) {
        // 根据配置 HttpServerConfig 中的实例数量（config.getInstance()）创建一个 ArrayList<HttpServer>，
        // 表示要启动的HTTP服务器实例列表
        List<HttpServer> instances = new ArrayList<>(config.getInstance());
        // 利用多线程处理请求
        for (int i = 0; i < config.getInstance(); i++) {
            // 调用 createHttpServer(config) 方法创建一个新的Vert.x HttpServer 实例。
            HttpServer httpServer = createHttpServer(config);
            // 将新创建的 HttpServer 添加到实例列表中。
            instances.add(httpServer);
        }
        // 设置 VertxHttpServer 对象的绑定地址为配置中指定的主机名和端口号。
        server.setBindAddress(new InetSocketAddress(config.getHost(), config.getPort()));
        // 这段代码主要是为了将HTTP请求分发到已注册的处理逻辑或订阅者，并确保每个请求都能得到正确的响应处理。
        server.setHttpServers(instances);
        // 再次遍历所有 HttpServer 实例，并调用 listen 方法来监听指定的网络地址和端口，准备接收HTTP请求。
        for (HttpServer httpServer : instances) {
            httpServer.listen(result -> {
                // 如果服务器成功启动，将在日志中记录一条信息，表明HTTP服务器已在指定地址启动。
                if (result.succeeded()) {
                    log.debug("startup http server on [{}]", server.getBindAddress());
                } else {
                    // 如果服务器启动失败，则将错误原因设置到 VertxHttpServer 的 lastError 属性，
                    // 并在日志中记录警告信息，包含失败的服务器地址以及具体的错误原因。
                    server.setLastError(result.cause().getMessage());
                    log.warn("startup http server on [{}] failed", server.getBindAddress(), result.cause());
                }
            });
        }

    }
}
